#include "save.h"

Save::FactoryMap Save::_factory;
Save::SaveInfo*  Save::_saveInfo = 0;
Save::LoadInfo*  Save::_loadInfo = 0;

void Save::UnregisterNodeTypes()
{
    foreach ( FactoryPair p, _factory )
        delete p.second;
}

void Save::SaveGraph( std::ostream& out, const Node* startingNode )
{
    SaveInfo info;
    info.map[ startingNode ] = 0;
    info.queue.push_back( startingNode );
    _saveInfo = &info;

    while ( !info.queue.empty() ) {
        Node* s = const_cast< Node* >( info.queue[ 0 ] );
        out << s->GetTypenameVirtual() << " " << _saveInfo->map[ s ] << " ";
        s->Data( out );
        info.queue.erase( info.queue.begin() );
    }

    _saveInfo = 0;
}

Save::Node* Save::LoadGraph( std::istream& in )
{
    LoadInfo info;
    _loadInfo = &info;

    Node* r = 0;
    while ( true ) {
        std::string type;
        uint id = 0;
        in >> type;
        in >> id;
        if ( type.length() == 0 )
            break;
        if ( _factory.find( type ) == _factory.end() )
            throw std::runtime_error( "Unrecognised save node type" );
        Node* s = _factory[ type ]->Load();
        s->Data( in );
        if ( !r )
            r = s;
        info.map[ id ] = s;
    }

    foreach ( Node** t, info.list ) {
        uint id = ( uint )*t;
        *t = info.map[ id ];
    }

    _loadInfo = 0;
    return r;
}

#define SAVE_PRIMITIVE_DEF( type ) \
void Save::Data( std::ostream& out, type t )  \
{                                                 \
    out << t << " ";                              \
}                                                 \
                                                  \
void Save::Data( std::istream& in, type & t ) \
{                                                 \
    in >> t;                                      \
}

SAVE_PRIMITIVE_DEF( int );
SAVE_PRIMITIVE_DEF( unsigned int );
SAVE_PRIMITIVE_DEF( short );
SAVE_PRIMITIVE_DEF( unsigned short );
SAVE_PRIMITIVE_DEF( long );
SAVE_PRIMITIVE_DEF( unsigned long );
SAVE_PRIMITIVE_DEF( char );
SAVE_PRIMITIVE_DEF( unsigned char );
SAVE_PRIMITIVE_DEF( signed char );
SAVE_PRIMITIVE_DEF( float );
SAVE_PRIMITIVE_DEF( double );

void Save::Data( std::ostream& out, const std::string& t )
{
    uint first = t.find_first_not_of( " " );
    uint last  = t.find_last_not_of( " " );
    if ( first == std::string::npos || t.length() == 0 ) {
        out << "0 ";
        return;
    }
    std::string trim = t.substr( first, last - first + 1 );
    out << trim.length() << " " << trim << " ";
}

void Save::Data( std::istream& in, std::string& t )
{
    t = "";
    uint len;
    in >> len;
    if ( !len )
        return;

    in.get();
    for ( uint i = 0; i < len; i++ ) {
        char c;
        in.get( c );
        t += c;
    }
}

void Save::Data( std::ostream& out, const Node& t )
{
    const_cast< Node& >( t ).Data( out );
}

void Save::Data( std::istream& in, Node& t )
{
    t.Data( in );
}

void Save::Data( std::ostream& out, const Node* t )
{
    if ( !t )
        out << "0 ";
    if ( _saveInfo->map.find( t ) == _saveInfo->map.end() ) {
        uint id = 1 + _saveInfo->map.size();
        _saveInfo->map[ t ] = id;
        _saveInfo->queue.push_back( t );
    }
    out << _saveInfo->map[ t ] << " ";
}

void Save::Data( std::istream& in, Node*& t )
{
    uint id;
    in >> id;
    if ( !id )
        t = 0;
    else {
        t = ( Node* )id;
        _loadInfo->list.push_back( &t );
    }
}
